package com.am.server.api.system.parameter.service.impl;

import com.am.server.api.system.parameter.annotation.SystemParameter;
import com.am.server.api.system.parameter.annotation.SystemParameterConstant;
import com.am.server.api.system.parameter.dao.cache.ParameterCacheDao;
import com.am.server.api.system.parameter.dao.rdb.ParameterDao;
import com.am.server.api.system.parameter.exception.DuplicateParameterException;
import com.am.server.api.system.parameter.model.entity.ParameterEntity;
import com.am.server.api.system.parameter.service.SynchronizeParameterService;
import com.am.server.common.config.CommonConfig;
import lombok.extern.slf4j.Slf4j;
import org.reflections.ReflectionUtils;
import org.reflections.Reflections;
import org.reflections.util.ReflectionUtilsPredicates;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author 阮雪峰
 */
@Slf4j
@Service
public class SynchronizeParameterServiceImpl implements SynchronizeParameterService {

    private final CommonConfig commonConfig;
    private final ParameterDao parameterDao;
    private final ParameterCacheDao parameterCacheDao;

    public SynchronizeParameterServiceImpl(CommonConfig commonConfig, ParameterDao parameterDao, ParameterCacheDao parameterCacheDao) {
        this.commonConfig = commonConfig;
        this.parameterDao = parameterDao;
        this.parameterCacheDao = parameterCacheDao;
    }

    @Override
    public void load() {
        Reflections reflections = new Reflections(commonConfig.getBasePackage());
        Set<Class<?>> clazz = reflections.getTypesAnnotatedWith(SystemParameterConstant.class);
        List<SystemParameter> list = checkedDuplicate(clazz);
        Map<String, ParameterEntity> mapByUniqueKey = parameterDao.findAll().stream().collect(Collectors.toMap(ParameterEntity::getUniqueKey, item -> item));

        List<ParameterEntity> parameterEntityList = list.stream()
                .map(systemParameter -> {
                    ParameterEntity parameterEntity;
                    String fullPathTemplate = "%s-%s";
                    String uniqueKey = systemParameter.uniqueKey();
                    if (!mapByUniqueKey.containsKey(uniqueKey)) {
                        parameterEntity = new ParameterEntity();
                        mapByUniqueKey.put(uniqueKey, parameterEntity);
                    } else {
                        parameterEntity = mapByUniqueKey.get(uniqueKey);
                    }

                    parameterEntity.setValue(systemParameter.value());
                    parameterEntity.setName(systemParameter.name());
                    parameterEntity.setUniqueKey(uniqueKey);
                    parameterEntity.setCatlog(systemParameter.catlog());
                    parameterEntity.setDescription(systemParameter.description());
                    parameterEntity.setType(systemParameter.type());
                    parameterEntity.setFullPath(String.format(fullPathTemplate, systemParameter.catlog(), systemParameter.name()));
                    parameterEntity.setDelete(Boolean.FALSE);
                    return parameterEntity;
                })
                .toList();

        parameterEntityList.forEach(item -> mapByUniqueKey.remove(item.getUniqueKey()));
        // 将已经删除的参数标记为删除状态
        mapByUniqueKey.values().forEach(item -> item.setDelete(Boolean.TRUE));
        parameterDao.saveAll(parameterEntityList);
        parameterDao.saveAll(mapByUniqueKey.values());

        parameterCacheDao.clear();
        parameterCacheDao.saveAll(parameterEntityList);
    }

    /**
     * 校验重复参数
     *
     * @param clz clz
     */
    private List<SystemParameter> checkedDuplicate(Set<Class<?>> clz) {
        List<Field> fields = new ArrayList<>();
        for (Class<?> item : clz) {
            fields.addAll(ReflectionUtils.getFields(item, ReflectionUtilsPredicates.withAnnotation(SystemParameter.class)));
        }
        List<SystemParameter> list = new ArrayList<>();
        Map<String, Field> fieldMap = new HashMap<>();
        for (Field field : fields) {
            SystemParameter systemParameter = field.getAnnotation(SystemParameter.class);
            if (fieldMap.containsKey(systemParameter.uniqueKey())) {
                throw new DuplicateParameterException(systemParameter.uniqueKey(), field.getDeclaringClass().getName(), fieldMap.get(systemParameter.uniqueKey()).getDeclaringClass().getName());
            } else {
                list.add(systemParameter);
                fieldMap.put(systemParameter.uniqueKey(), field);
            }
        }
        return list;
    }

}
